home *** CD-ROM | disk | FTP | other *** search
- /* nas.c --- XEmacs support for the Network Audio System server.
- *
- * Author: Richard Caley <R.Caley@ed.ac.uk>
- *
- * Copyright 1994 Free Software Foundation, Inc.
- * Copyright 1993 Network Computing Devices, Inc.
- *
- * Permission to use, copy, modify, distribute, and sell this software and
- * its documentation for any purpose is hereby granted without fee, provided
- * that the above copyright notice appear in all copies and that both that
- * copyright notice and this permission notice appear in supporting
- * documentation, and that the name Network Computing Devices, Inc. not be
- * used in advertising or publicity pertaining to distribution of this
- * software without specific, written prior permission.
- *
- * THIS SOFTWARE IS PROVIDED 'AS-IS'. NETWORK COMPUTING DEVICES, INC.,
- * DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING WITHOUT
- * LIMITATION ALL IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
- * PARTICULAR PURPOSE, OR NONINFRINGEMENT. IN NO EVENT SHALL NETWORK
- * COMPUTING DEVICES, INC., BE LIABLE FOR ANY DAMAGES WHATSOEVER, INCLUDING
- * SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES, INCLUDING LOSS OF USE, DATA,
- * OR PROFITS, EVEN IF ADVISED OF THE POSSIBILITY THEREOF, AND REGARDLESS OF
- * WHETHER IN AN ACTION IN CONTRACT, TORT OR NEGLIGENCE, ARISING OUT OF OR IN
- * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
- */
-
- /* Synched up with: Not in FSF. */
-
- /* There are four compile-time options.
- *
- * XTOOLKIT This will be part of an Xt program.
- *
- * XTEVENTS The playing will be supervised asynchronously by the Xt event
- * loop. If not set, playing will be completed within the call
- * to play_file etc.
- *
- * ROBUST_PLAY Causes errors in nas to be caught. This means that the
- * program will attempt not to die if the nas server does.
- *
- * CACHE_SOUNDS Causes the sounds to be played in buckets in the NAS
- * server. They are named by their comment field, or if that is
- * empty by the filename, or for play_sound_data by a name made up
- * from the sample itself.
- */
-
- /* CHANGES:
- * 10/8/94, rjc Changed names from netaudio to nas
- * Added back asynchronous play if nas library has
- * correct error facilities.
- * 4/11/94, rjc Added wait_for_sounds to be called when user wants to
- * be sure all play has finished.
- */
-
- #if __STDC__ || defined (STDC_HEADERS)
-
- # include <stdlib.h>
- # include <unistd.h>
- # include <stdarg.h>
-
- #endif
-
- #include <stdio.h>
- #include <string.h>
- #include <config.h> /* for CONST in syssignal.h (neal@ctd.comsat.com) */
- #include "syssignal.h"
-
- #include <audio/audiolib.h>
- #include <audio/soundlib.h>
- #include <audio/snd.h>
- #include <audio/fileutil.h>
-
- #ifdef emacs
-
- # include <config.h>
- # include "lisp.h"
-
- # define XTOOLKIT
- # define XTEVENTS
- # define ROBUST_PLAY
- # define CACHE_SOUNDS
-
- /*
- * For old NAS libraries, force playing to be synchronous
- * and declare the long jump point locally.
- */
-
- # if defined (NAS_NO_ERROR_JUMP)
-
- # undef XTEVENTS
-
- # include <setjmp.h>
- jmp_buf AuXtErrorJump;
- # endif
-
- /* The GETTEXT is correct. --ben */
- # define warn(str) warn_when_safe (Qnas, Qwarning, "nas: %s ", GETTEXT (str))
-
- # define play_sound_file nas_play_sound_file
- # define play_sound_data nas_play_sound_data
- # define wait_for_sounds nas_wait_for_sounds
- # define init_play nas_init_play
- # define close_down_play nas_close_down_play
-
- #else /* !emacs */
- # define warn(str) fprintf (stderr, "%s\n", (str))
- # define CONST const
- #endif /* emacs */
-
- #ifdef XTOOLKIT
- # include <X11/Intrinsic.h>
- # include <audio/Xtutil.h>
- #endif
-
- #if defined (ROBUST_PLAY)
- static AuBool CatchIoErrorAndJump (AuServer *aud);
- static AuBool CatchErrorAndJump (AuServer *aud, AuErrorEvent *event);
- SIGTYPE sigpipe_handle (int signo);
- #endif
-
- extern Lisp_Object Vsynchronous_sounds;
-
- static Sound SoundOpenDataForReading (unsigned char *data, int length);
-
- static AuServer *aud;
-
- /* count of sounds currently being played. */
- static int sounds_in_play;
-
-
- #ifdef XTOOLKIT
- static Display *aud_server;
- static XtInputId input_id;
- #else
- static char *aud_server;
- #endif /* XTOOLKIT */
-
- char *
- init_play (
- #ifdef XTOOLKIT
- Display *display
- #else
- char *server
- #endif
- )
- {
- char *err_message;
- SIGTYPE (*old_sigpipe) ();
-
- #ifdef XTOOLKIT
- char * server = DisplayString (display);
- XtAppContext app_context = XtDisplayToApplicationContext (display);
-
- aud_server = display;
- #else
-
- aud_server = server;
- #endif
-
- #ifdef ROBUST_PLAY
- old_sigpipe = signal (SIGPIPE, sigpipe_handle);
- if (setjmp (AuXtErrorJump))
- {
- signal (SIGPIPE, old_sigpipe);
- #ifdef emacs
- start_interrupts ();
- #endif
- return "error in NAS";
- }
- #endif
-
- #if defined (ROBUST_PLAY) && !defined (NAS_NO_ERROR_JUMP)
- AuDefaultIOErrorHandler = CatchIoErrorAndJump;
- AuDefaultErrorHandler = CatchErrorAndJump;
- #endif
-
- #ifdef emacs
- stop_interrupts ();
- #endif
- aud = AuOpenServer (server, 0, NULL, 0, NULL, &err_message);
- #ifdef emacs
- start_interrupts ();
- #endif
- if (!aud)
- {
- #ifdef ROBUST_PLAY
- signal (SIGPIPE, old_sigpipe);
- #endif
- if (err_message == NULL)
- return "Can't connect to audio server";
- else
- return err_message;
- }
-
- #if defined (ROBUST_PLAY)
- # if defined (NAS_NO_ERROR_JUMP)
- aud->funcs.ioerror_handler = CatchIoErrorAndJump;
- aud->funcs.error_handler = CatchErrorAndJump;
- # else /* !NAS_NO_ERROR_JUMP */
- AuDefaultIOErrorHandler = NULL;
- AuDefaultErrorHandler = NULL;
- # endif
- #endif
-
- #ifdef XTEVENTS
- input_id = AuXtAppAddAudioHandler (app_context, aud);
- #endif
-
- #ifdef CACHE_SOUNDS
- AuSetCloseDownMode (aud, AuCloseDownRetainPermanent, NULL);
- #endif
-
- #ifdef ROBUST_PLAY
- signal (SIGPIPE, old_sigpipe);
- #endif
-
- sounds_in_play = 0;
-
- return NULL;
- }
-
- void
- close_down_play (void)
-
- {
- AuCloseServer (aud);
- warn ("disconnected from audio server");
- }
-
- /********************************************************************\
- * *
- * Callback which is run when the sound finishes playing. *
- * *
- \********************************************************************/
-
- static void
- doneCB (AuServer *aud,
- AuEventHandlerRec *handler,
- AuEvent *ev,
- AuPointer data)
- {
- int *in_play_p = (int *) data;
-
- (*in_play_p) --;
- }
-
- #ifdef CACHE_SOUNDS
-
- /********************************************************************\
- * *
- * Play a sound by playing the relevant bucket, if any or *
- * downloading it if not. *
- * *
- \********************************************************************/
-
- static void
- do_caching_play (Sound s,
- int volume,
- unsigned char *buf)
-
- {
- AuBucketAttributes *list, b;
- AuBucketID id;
- int n;
-
- AuSetString (AuBucketDescription (&b),
- AuStringLatin1, strlen (SoundComment (s)), SoundComment (s));
-
- list = AuListBuckets (aud, AuCompCommonDescriptionMask, &b, &n, NULL);
-
- if (list == NULL)
- {
- unsigned char *my_buf;
-
- if (buf==NULL)
- {
- if ((my_buf=malloc (SoundNumBytes (s)))==NULL)
- {
- return;
- }
-
- if (SoundReadFile (my_buf, SoundNumBytes (s), s) != SoundNumBytes (s))
- {
- free (my_buf);
- return;
- }
- }
- else
- my_buf=buf;
-
- id = AuSoundCreateBucketFromData (aud,
- s,
- my_buf,
- AuAccessAllMasks,
- NULL,
- NULL);
- if (buf == NULL)
- free (my_buf);
- }
- else /* found cached sound */
- {
- id = AuBucketIdentifier (list);
- AuFreeBucketAttributes (aud, n, list);
- }
-
- sounds_in_play++;
-
- AuSoundPlayFromBucket (aud,
- id,
- AuNone,
- AuFixedPointFromFraction (volume, 100),
- doneCB, (AuPointer) &sounds_in_play,
- 1,
- NULL, NULL,
- NULL, NULL);
-
- }
- #endif /* CACHE_SOUNDS */
-
-
- void
- wait_for_sounds (void)
-
- {
- AuEvent ev;
-
- while (sounds_in_play>0)
- {
- AuNextEvent (aud, AuTrue, &ev);
- AuDispatchEvent (aud, &ev);
- }
- }
-
- int
- play_sound_file (char *sound_file,
- int volume)
- {
- SIGTYPE (*old_sigpipe) ();
-
- #ifdef ROBUST_PLAY
- old_sigpipe=signal (SIGPIPE, sigpipe_handle);
- if (setjmp (AuXtErrorJump))
- {
- signal (SIGPIPE, old_sigpipe);
- return 0;
- }
- #endif
-
- if (aud==NULL)
- if (aud_server != NULL)
- {
- char *m;
- /* attempt to reconect */
- if ((m=init_play (aud_server))!= NULL)
- {
-
- #ifdef ROBUST_PLAY
- signal (SIGPIPE, old_sigpipe);
- #endif
- return 0;
- }
- }
- else
- {
- warn ("Attempt to play with no audio init\n");
- #ifdef ROBUST_PLAY
- signal (SIGPIPE, old_sigpipe);
- #endif
- return 0;
- }
-
- #ifndef CACHE_SOUNDS
- sounds_in_play++;
- AuSoundPlayFromFile (aud,
- sound_file,
- AuNone,
- AuFixedPointFromFraction (volume,100),
- doneCB, (AuPointer) &sounds_in_play,
- NULL,
- NULL,
- NULL,
- NULL);
- #else
- /* Cache the sounds in buckets on the server */
-
- {
- Sound s;
-
- if ((s = SoundOpenFileForReading (sound_file))==NULL)
- {
- #ifdef ROBUST_PLAY
- signal (SIGPIPE, old_sigpipe);
- #endif
- return 0;
- }
-
- if (SoundComment (s) == NULL || SoundComment (s)[0] == '\0')
- {
- SoundComment (s) = FileCommentFromFilename (sound_file);
- }
-
- do_caching_play (s, volume, NULL);
-
- SoundCloseFile (s);
-
- }
- #endif /* CACHE_SOUNDS */
-
- #ifndef XTEVENTS
- wait_for_sounds ();
- #else
- if (!NILP (Vsynchronous_sounds))
- {
- wait_for_sounds ();
- }
- #endif
-
- #ifdef ROBUST_PLAY
- signal (SIGPIPE, old_sigpipe);
- #endif
-
- return 1;
- }
-
- int
- play_sound_data (unsigned char *data,
- int length,
- int volume)
- {
- Sound s;
- int offset;
- SIGTYPE (*old_sigpipe) ();
-
- #if !defined (XTEVENTS)
- AuEvent ev;
- #endif
-
- #ifdef ROBUST_PLAY
- old_sigpipe = signal (SIGPIPE, sigpipe_handle);
- if (setjmp (AuXtErrorJump) !=0)
- {
- signal (SIGPIPE, old_sigpipe);
- return 0;
- }
- #endif
-
-
- if (aud == NULL)
- if (aud_server != NULL)
- {
- char *m;
- /* attempt to reconect */
- if ((m = init_play (aud_server)) != NULL)
- {
- #ifdef ROBUST_PLAY
- signal (SIGPIPE, old_sigpipe);
- #endif
- return 0;
- }
- }
- else
- {
- warn ("Attempt to play with no audio init\n");
- #ifdef ROBUST_PLAY
- signal (SIGPIPE, old_sigpipe);
- #endif
- return 0;
- }
-
- if ((s=SoundOpenDataForReading (data, length))==NULL)
- {
- warn ("unknown sound type");
- #ifdef ROBUST_PLAY
- signal (SIGPIPE, old_sigpipe);
- #endif
- return 0;
- }
-
- if (SoundFileFormat (s) == SoundFileFormatSnd)
- {
- /* hack, hack */
- offset = ((SndInfo *) (s->formatInfo))->h.dataOffset;
- }
- else
- {
- warn ("only understand snd files at the moment");
- SoundCloseFile (s);
- #ifdef ROBUST_PLAY
- signal (SIGPIPE, old_sigpipe);
- #endif
- return 0;
- }
-
- #ifndef CACHE_SOUNDS
- sounds_in_play++;
- AuSoundPlayFromData (aud,
- s,
- data+offset,
- AuNone,
- AuFixedPointFromFraction (volume,100),
- doneCB, (AuPointer) &sounds_in_play,
- NULL,
- NULL,
- NULL,
- NULL);
- #else
- /* Cache the sounds in buckets on the server */
-
- {
- do_caching_play (s, volume, data+offset);
- }
- #endif /* CACHE_SOUNDS */
-
-
- #ifndef XTEVENTS
- wait_for_sounds ();
- #else
- if (!NILP (Vsynchronous_sounds))
- {
- wait_for_sounds ();
- }
- #endif
-
- SoundCloseFile (s);
-
- #ifdef ROBUST_PLAY
- signal (SIGPIPE, old_sigpipe);
- #endif
-
- return 1;
- }
-
- #if defined (ROBUST_PLAY)
-
- /********************************************************************\
- * *
- * Code to protect the client from server shutdowns. *
- * *
- * This is unbelievably horrible. *
- * *
- \********************************************************************/
-
- static AuBool
- CatchIoErrorAndJump (AuServer *old_aud)
- {
- if (old_aud)
- warn ("Audio Server connection broken");
- else
- warn ("Audio Server connection broken because of signal");
-
- #ifdef XTEVENTS
- #ifdef XTOOLKIT
- {
- AuXtAppRemoveAudioHandler (aud, input_id);
- }
- #endif
-
- if (aud)
- AuCloseServer (aud);
- aud = NULL;
- sounds_in_play = 0;
-
- longjmp (AuXtErrorJump, 1);
-
- #else /* not XTEVENTS */
-
- if (aud)
- AuCloseServer (aud);
- aud = NULL;
- sounds_in_play = 0;
- longjmp (AuXtErrorJump, 1);
-
- #endif /* XTEVENTS */
- }
-
- SIGTYPE
- sigpipe_handle (int signo)
- {
- CatchIoErrorAndJump (NULL);
- }
-
- static AuBool
- CatchErrorAndJump (AuServer *old_aud,
- AuErrorEvent *event)
- {
- return CatchIoErrorAndJump (old_aud);
- }
-
- #endif /* ROBUST_PLAY */
-
- /********************************************************************\
- * *
- * This code is here because the nas Sound library doesn't *
- * support playing from a file buffered in memory. It's a fairly *
- * direct translation of the file-based equivalent. *
- * *
- * Since we don't have a filename, samples with no comment field *
- * are named by a section of their content. *
- * *
- \********************************************************************/
-
- /* Create a name from the sound. */
-
- static char *
- NameFromData (CONST unsigned char *buf,
- int len)
-
- {
- unsigned char name[9];
- int i;
- char *s;
-
- buf+=len/2;
- len -= len/2;
-
- i=0;
- while (i<8 && len >0)
- {
- while (*buf < 32 && len>0)
- {
- buf++;
- len--;
- }
- name[i]= *buf;
- i++;
- buf++;
- len--;
- }
-
- name[i]='\0';
-
- if (i==8)
- {
- strcpy (s=malloc (10), name);
- }
- else
- {
- strcpy (s=malloc (15), "short sound");
- }
-
- return s;
- }
-
- /* Code to do a pseudo-open on a data buffer. Only for snd files at the
- moment.
- */
-
- static SndInfo *
- SndOpenDataForReading (CONST char *data,
- int length)
-
- {
- SndInfo *si;
- int size;
-
- if (!(si = (SndInfo *) malloc (sizeof (SndInfo))))
- return NULL;
-
- si->comment = NULL;
- si->writing = 0;
-
- memcpy (&si->h, data, sizeof (SndHeader));
-
- if (LITTLE_ENDIAN)
- {
- char n;
-
- swapl (&si->h.magic, n);
- swapl (&si->h.dataOffset, n);
- swapl (&si->h.dataSize, n);
- swapl (&si->h.format, n);
- swapl (&si->h.sampleRate, n);
- swapl (&si->h.tracks, n);
- }
-
- if (si->h.magic != SND_MAGIC_NUM)
- {
- free (si);
- return NULL;
- }
-
- size = si->h.dataOffset - sizeof (SndHeader);
-
- if (size)
- {
- if (!(si->comment = (char *) malloc (size + 1)))
- {
- free (si);
- return NULL;
- }
-
- memcpy (si->comment, data+sizeof (SndHeader), size);
-
- *(si->comment + size) = 0;
- if (*si->comment == '\0')
- si->comment =
- NameFromData (data+si->h.dataOffset, length-si->h.dataOffset);
- }
- else
- si->comment = NameFromData (data+si->h.dataOffset, length-si->h.dataOffset);
-
- si->h.dataSize = length-si->h.dataOffset;
-
- si->fp=NULL;
-
- return si;
- }
-
- static Sound
- SoundOpenDataForReading (unsigned char *data,
- int length)
-
- {
- Sound s;
-
- if (!(s = (Sound) malloc (sizeof (SoundRec))))
- return NULL;
-
- if ((s->formatInfo = SndOpenDataForReading (data, length))==NULL)
- {
- free (s);
- return NULL;
- }
-
-
- if (!(SoundFileInfo[SoundFileFormatSnd].toSound) (s))
- {
- SndCloseFile (s->formatInfo);
- free (s);
- return NULL;
- }
-
- return s;
- }
-
-